fn main() {
    let outer_var = 42;

    // A regular function can't refer to varibles in the enclosing environment
    // fn function(i:i32) -> i32 {i + outer_var}
    // TODO: uncomment the line above and see the compiler error, the compiler
    // suggests that we define a closure instead.

    // Closures are anonymous, here we are binding them to references.
    // Annotation is identical to function annotation but is optional.
    // as are the `{}` wrapping the body. these nameless functions
    // are assigned to appropriately named variables.
    let closure_annotated = |i:i32| -> i32 {i + outer_var};
    let closure_inferred = |i| i + outer_var;

    // call the closures
    println!("closure_annotated: {:?}", closure_annotated(1));
    println!("closure_inferred: {:?}", closure_inferred(1));

    // once closure's type has been inferred, it cannot be inferred again with another type
    // println!("cannot reuse closure_inferred with another type: {:?}", closure_inferred(1i64));
    // TODO: uncomment the line above and see the compiler error.

    // a closure taking no arguments which returns an `i32`.
    // the return type is inferred.
    let one = || 1;
    println!("closure returning one:{}", one());
}